package com.opennews.openplatform.myspringbootmvccore.controller;

import com.opennews.openplatform.myspringbootcore.dto.ApiResponse;
import com.opennews.openplatform.myspringbootcore.exception.EntityNotFoundException;
import com.opennews.openplatform.myspringbootmvccore.helper.HttpResponseHelper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.resource.NoResourceFoundException;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@RequiredArgsConstructor
@ControllerAdvice
public class GeneralGlobalExceptionHandler {
    protected HttpServletResponse response;
    protected HttpResponseHelper httpResponseHelper = new HttpResponseHelper(response);

    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<ApiResponse<Map>> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException ex) {
        return httpResponseHelper.getErrorResponse(HttpStatus.METHOD_NOT_ALLOWED, Map.ofEntries(
                Map.entry("message", "Unsupported HTTP method: " + ex.getMethod())
        ));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ApiResponse<Map>> handleValidationExceptions(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();

        // Collects validation error messages from all invalid fields.
        for (FieldError error : ex.getBindingResult().getFieldErrors()) {
            errors.put(error.getField(), error.getDefaultMessage());
        }

        return httpResponseHelper.getErrorResponse(HttpStatus.BAD_REQUEST, errors);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseEntity<ApiResponse<Map>> handleMissingServletRequestParameterException(MissingServletRequestParameterException ex) {
        return httpResponseHelper.getErrorResponse(HttpStatus.BAD_REQUEST, Map.ofEntries(
                Map.entry("message", "Missing required parameter: " + ex.getParameterName())
        ));
    }

    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<ApiResponse<Map>> handleEntityNotFoundException(EntityNotFoundException ex) {
        return httpResponseHelper.getErrorResponse(HttpStatus.NOT_FOUND, Map.ofEntries(
                Map.entry("message", ex.getMessage())
        ));
    }

    @ExceptionHandler(NoResourceFoundException.class)
    public ResponseEntity<ApiResponse<Map>> handleNoResourceFoundException(NoResourceFoundException ex) {
        return httpResponseHelper.getErrorResponse(HttpStatus.NOT_FOUND, Map.ofEntries(
                Map.entry("message", ex.getMessage())
        ));
    }

    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<ApiResponse<Map>> handleHttpMessageNotReadableException() {
        return httpResponseHelper.getErrorResponse(HttpStatus.BAD_REQUEST, Map.ofEntries(
                Map.entry("message", "Invalid request body.")
        ));
    }

    @ExceptionHandler(IOException.class)
    public ResponseEntity<ApiResponse<Map>> handleIOException() {
        return httpResponseHelper.getErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, Map.ofEntries(
                Map.entry("message", "File IO error")
        ));
    }

    @ExceptionHandler(Exception.class)
    public ResponseEntity<ApiResponse<Map>> handleGenericException(Exception ex) {
        System.out.println(ex.getMessage());

        return httpResponseHelper.getErrorResponse(HttpStatus.INTERNAL_SERVER_ERROR, Map.ofEntries(
                Map.entry("message", "An unexpected error occurred: ")
        ));
    }
}